home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Network Support Library
/
RoseWare - Network Support Library.iso
/
apidev
/
netman.arc
/
READ.ME
< prev
Wrap
Text File
|
1989-08-22
|
36KB
|
1,181 lines
Network Mandelbrot Set
This text documents the programs LEADER.PAS and FOLLOWER.PAS are written in
Borland Turbo Pascal version 5.0. They use standard file locking to enable
distributed processing of the Mandelbrot set over virtually any PC-based LAN.
The code is presented more as an example of programming and framework for
personal enhancement than it is as a full-featured software package (although
the extension of the user interface and addition of other capabilities could
easily make it one with only a modest programming effort).
What is the Mandelbrot Set?
The Mandelbrot set has been described as "one of the most beautiful objects of
mathematics". Technically it is a set of points described by iterative
equations. It is a member of the family of fractals, equations so named
because they describe "fractional" dimensions (rather than linear forms such as
Cartesian functions).
There are many excellent sources on this exciting and emerging field of
"chaology" (the study of chaos) of which fractals are only a facet (see Chaos:
Making a New Science by James Gleick, Viking). Here only a short description
will be presented. The steps are as follows:
1. Map a computer screen so that every pixel corresponds to a corresponding
real number position on a coordinate plane not larger than 2 units from the
origin in any direction.
2. Repeat the following algorithm for every pixel:
2a. Assign the X value of the point to a variable (say, of the same name), as
with Y.
2b. Initialize two real number variables (say, ZR and ZI) to zero.
2c. Replace ZR with the square of itself subtracted from the square of ZI plus
X, that is
ZR -> ZR^2 - ZI^2 + X
2d. Replace ZI with twice ZR multiplied by itself plus Y, that is,
ZI -> 2 * ZR * ZI + Y
2e. Repeat the above operations (from 2a) for at most some arbitrary large
number of times consistent for every pixel (say I) or while the square root of
the sum of the squares of ZR and ZI is less than two, that is,
sqrt(ZR^2 + ZI^2) < 2
3. Color the corresponding pixels on the screen according to the number of
iterations spent in the loop (say, I') above (2a-2e) per pixel.
Now, much underlying mathematical meaning has been removed from the process
described above; it only hints of "linear distance" and "imaginary numbers"
etc., concepts that are not crucial to programming it. However, the process
certainly seems rather arbitary without this background.
By convention, the "color" referred to in step 3 is that which results when I'
is mapped on to the color spectrum with blue for low values and red for high
values.
Now, for coordinates where I' = I (that is, the loop does not terminate
prematurely because the condition under 2e is satisfied) are presumed to lie in
the "Mandelbrot Set". Technically this names those coordinates that NEVER
satisfy the condition but on computers (as not necessarily in mathematics)
infinity is to be avoided, or at least until processors achieve transcendental
computation.
The Approach
Two programs named LEADER and FOLLOWER are used, analogous to "servers and
clients" or "masters and slaves". They were written in Turbo 5.0 Pascal
adhering to DOS file-locking standards (provided by the program SHARE). Here
is the general flow of events:
- LEADER -
1. Displays a title screen.
2. Creates a file FRACTAL.DAT that corresponds in size to that of the internal
representation of the screen (for example, a low-resolution CGA mode takes 16K,
so the file is padded to that size).
3. Creates the auxiliary file VALUES.DAT that contains numerical descriptions
of screen dimensions and formats.
- FOLLOWER -
4. Meanwhile has been waiting for the cue to start, which is the presence of
the VALUES.DAT file.
4. Springs to life the moment it detects the existence of the auxiliary file
written by the LEADER and saves the data therein to internal memory, and enters
the main loop.
5. (In the main loop) determines and takes the next available (uncomputed)
screen line by interpreting and updating information in VALUES.DAT.
6. Processes the line and sends the raw screen data to the corresponding
position in FRACTAL.DAT.
7. Gives a visual cue as to the overall progress of work and repeats the last
two steps if there is unfinished work. Otherwise it is the last to finish and
deletes the VALUES.DAT file.
- LEADER -
8. Meanwhile has been providing a visual cue of the progress of FOLLOWER(s).
9. Springs to life again from this dormancy as VALUES.DAT vanishes when deleted
by the last FOLLOWER.
10. Displays the results in FRACTAL.DAT and allows the cycle to be repeated for
a new coordinate region.
All of this is done in a way that addresses considerations involved with
concurrent file usage and places no upper limit on the number of FOLLOWER
participants whatsoever (of course at least one must exist).
Notes on Programs
These descriptions refer sequentially to the lines in the programs LEADER and
FOLLOWER.
- LEADER -
1 {$define WEAVE}
2
3 program Leader;
4
5 uses Drivers, Graph, DOS, CRT;
6
7 const GraphMode = MCGAC0;
8 GraphDriver = MCGA;
9 Block = 50;
10 ScreenMap = $b800;
11 BitsPerHue = 2;
12 Threshold = 4;
13 Path='.';
14 Painting = 'FRACTAL.DAT';
15 Framework = 'VALUES.DAT';
16
17 {$ifdef WEAVE}
18 Interlace = true;
19 ScreenMap2 = $ba00;
20 HalfLine = 100;
21 {$else}
22 Interlace = false;
23 {$endif}
24
25 const No_Error = $00;
26 Not_Found = $02;
27 Access_Denied = $05;
28
29 const Read_Only = $00;
30 Write_Only = $01;
31 Read_Write = $02;
32 Deny_All = $10;
33 Deny_Write = $20;
34 Deny_Read = $30;
35 Deny_None = $40;
36
37 type Border = (Upper, Lower, Leftmost, Rightmost);
38 Count_Type = byte;
39 Size_Type = word;
40 Real_Type = real;
41
42 var Hues: byte;
43 Fence: array[Border] of Real_Type;
44
45 procedure Arrive;
46
47 procedure Property;
48
49 var Xasp,
50 Yasp: word;
51 Width,
52 Height: longint;
53 Adjust: real;
54
55 begin
56 GetAspectRatio(Xasp, Yasp);
57 Width:=(GetMaxX + 1) * Xasp;
58 Height:=(GetMaxY + 1) * Yasp;
59 if Width > Height
60 then begin
61 Adjust:=2 * (Width / Height);
62 Fence[Upper]:=2;
63 Fence[Lower]:=-2;
64 Fence[Leftmost]:=-Adjust;
65 Fence[Rightmost]:=Adjust;
66 end
67 else begin
68 Adjust:=2 * (Height / Width);
69 Fence[Upper]:=Adjust;
70 Fence[Lower]:=-Adjust;
71 Fence[Leftmost]:=-2;
72 Fence[Rightmost]:=2;
73 end;
74 end;
75
76 var Mode,
77 Driver,
78 Result: integer;
79
80 begin
81 Mode:=GraphMode;
82 Driver:=GraphDriver;
83 Result:=RegisterBGIdriver(@CGADriverProc);
84 InitGraph(Driver, Mode, Path);
85 Hues:=GetMaxColor + 1;
86 Property;
87 end;
88
89 type Shape = record
90 Sound: boolean;
91 ForeV,
92 RearV: Size_Type;
93
94 H,
95 V: Size_Type;
96 Most: Count_Type;
97 BitPixel,
98 PixelByte: byte;
99 ByteLine: word;
100 Top,
101 Left,
102 YInc,
103 XInc: Real_Type;
104 Weave: boolean;
105 end;
106
107 var Seed: Shape;
108 Canvas: file;
109
110 const Outgoing = true;
111 Incoming = false;
112
113 {$ifdef WEAVE}
114
115 procedure Door(Outgoing: boolean);
116
117 var Size: word;
118 Screen: pointer;
119
120 begin
121 Screen:=Ptr(ScreenMap, $0);
122 with Seed
123 do begin
124 Size:=Block * ByteLine;
125 if Outgoing
126 then begin
127 Rewrite(Canvas, ByteLine);
128 BlockWrite(Canvas, Screen^, HalfLine);
129 Screen:=Ptr(ScreenMap2, 0);
130 BlockWrite(Canvas, Screen^, HalfLine);
131 end
132 else begin
133 FileMode:=Read_Only + Deny_None;
134 Reset(Canvas, ByteLine);
135 BlockRead(Canvas, Screen^, HalfLine);
136 Screen:=Ptr(ScreenMap2, 0);
137 BlockRead(Canvas, Screen^, HalfLine);
138 end;
139 end;
140 Close(Canvas);
141 end;
142
143 {$else}
144
145 procedure Door(Outgoing: boolean);
146
147 var Size: word;
148 Lines: Size_Type;
149 Screen: pointer;
150
151 begin
152 Screen:=Ptr(ScreenMap, $0);
153 with Seed
154 do begin
155 Size:=Block * ByteLine;
156 if Outgoing
157 then Rewrite(Canvas, ByteLine)
158 else begin
159 FileMode:=Read_Only + Deny_None;
160 Reset(Canvas, ByteLine);
161 end;
162 Lines:=V1;
163 repeat
164 if Outgoing
165 then BlockWrite(Canvas, Screen^, Block)
166 else BlockRead(Canvas, Screen^, Block);
167 Inc(longint(Screen), Size);
168 Dec(Lines, Block);
169 until (Lines = 0)
170 end;
171 Close(Canvas);
172 end;
173
174 {$endif}
175
176 procedure Blend;
177
178 begin
179 end;
180
181 procedure Cultivate;
182
183 const On = true;
184 Off = false;
185
186 procedure Plant;
187
188 const BitByte = 8;
189
190 var Notice: file of Shape;
191
192 begin
193 with Seed
194 do begin
195 Sound:=Off;
196 V:=GetMaxY + 1;
197 ForeV:=V;
198 RearV:=V;
199 H:=GetMaxX + 1;
200 Most:=Threshold;
201 BitPixel:=BitsPerHue;
202 PixelByte:=BitByte div BitPixel;
203 ByteLine:=H div PixelByte;
204 Top:=Fence[Upper];
205 Left:=Fence[Leftmost];
206 YInc:=(Fence[Upper] - Fence[Lower]) / V;
207 XInc:=(Fence[Rightmost] - Fence[Leftmost]) / H;
208 Weave:=Interlace;
209 end;
210 Assign(Canvas, Painting);
211 Door(Outgoing);
212 Assign(Notice, Framework);
213 Rewrite(Notice);
214 Write(Notice, Seed);
215 Close(Notice);
216 end;
217
218 procedure Grow;
219
220 type Header = record
221 Sound: boolean;
222 AtV,
223 ToV: Size_Type;
224 end;
225
226 var Eye: file of Header;
227
228 procedure Ready;
229
230 begin
231 FileMode:= Read_Write + Deny_None;
232 Assign(Eye, Framework);
233 end;
234
235 var Line: string;
236
237 function Ripe: boolean;
238
239 var Result: word;
240 Place: Header;
241
242 begin
243 {$i-}
244 Reset(Eye);
245 {$i+}
246 Result:=IOResult;
247 if (Result = No_Error)
248 then begin
249 Read(Eye, Place);
250 Close(Eye);
251 Str(Place.AtV, Line);
252 end;
253 Ripe:=(Result = Not_Found);
254 end;
255
256 const Time = 500;
257
258 begin
259 Ready;
260 Line:='';
261 repeat
262 SetColor(White);
263 OutTextXY(0,0,Line);
264 Delay(Time);
265 SetColor(Black);
266 OutTextXY(0,0,Line);
267 until Ripe;
268 end;
269
270 procedure Harvest;
271
272 begin
273 Door(Incoming);
274 end;
275
276 begin
277 Plant;
278 Grow;
279 Harvest;
280 end;
281
282 procedure NewRegion;
283
284 const Step = 4;
285
286 const Scan = #0;
287 Enter = #13;
288 Up = #72;
289 Down = #80;
290 Left = #75;
291 Right = #77;
292 Reduce = #115;
293 Enlarge = #116;
294
295 var Key: char;
296 Xasp,
297 Yasp: word;
298 X,
299 Y,
300 Width,
301 Height: Size_Type;
302 Ratio: Real_Type;
303
304 begin
305 X:=GetMaxX div 2;
306 Y:=GetMaxY div 2;
307 Width:=GetMaxX div 2;
308 Ratio:=Seed.V / Seed.H;
309 SetWriteMode(XORPut);
310 SetColor(Random(GetMaxColor) + 1);
311 repeat
312 Height:=round(Width * Ratio);
313 Rectangle(X - Width, Y - Height, X + Width, Y + Height);
314 Key:=ReadKey;
315 Rectangle(X - Width, Y - Height, X + Width, Y + Height);
316 if Key = Scan
317 then begin
318 Key:=ReadKey;
319 case Key
320 of Up: Dec(Y, Step);
321 Left: Dec(X, Step);
322 Right: Inc(X, Step);
323 Down: Inc(Y, Step);
324 Reduce: Dec(Width, Step);
325 Enlarge: Inc(Width, Step);
326 end;
327 end;
328 until Key = Enter;
329 Fence[Upper]:=Fence[Upper] - Seed.YInc * (Y - Height);
330 Fence[Lower]:=Fence[Lower] + Seed.YInc * (GetMaxY - Y - Height);
331 Fence[Leftmost]:=Fence[Leftmost] + Seed.XInc * (X - Width);
332 Fence[Rightmost]:=Fence[Rightmost] - Seed.XInc * (GetMaxX - X - Width);
333 end;
334
335 procedure Depart;
336
337 begin
338 CloseGraph;
339 end;
340
341 var key:char;
342
343 begin
344 Arrive;
345 repeat
346 Cultivate;
347 NewRegion;
348 until false;
349 Depart;
350 end.
Comments
WEAVE: symbol that indicates the use of interlacing in
graphics mode
Untyped constants defined in lines 7 - 23:
GraphMode (TP constant) defines the mode for display
GraphDriver (TP constant) defines the driver for display
Block size of I/O block operations involving the screen; must
divide the number of lines in the selected GraphMode
ScreenMap memory address of the screen
BitsPerHue the number of bits required to represent all colors in
the selected GraphMode
Threshold the variable I as defined above
Path used to locate BGI files in call to InitGraph;
necessary only if the link module Drivers is not used
Painting name of file containing raw graphics data
Framework name of auxiliary file
Interlace declared true if WEAVE is defined, false otherwise
ScreenMap2 memory address of second half of interlaced screen
(declared only if WEAVE is defined)
HalfLine the screen line that separates the two interlaced
halves of the screen (declared only if WEAVE is
defined)
Lines 25 - 35 define constants used for TP error codes and specifying DOS file
sharing modes. The latter are used with the Turbo Pascal variable FileMode
provided expressly for this purpose. The constants in lines 29 - 31 determine
the access mode (read and/or write). The other constants in lines 32 - 35
reflect file sharing modes as defined by DOS in version 3.1 and later as
provided by the SHARE program. They are used both to determine whether a file
is accessable when OPENed and to specify the availability of the file with
external (non-local) access. The mode remains in effect until the file is
subsequently CLOSEd and is passed to DOS from Pascal only with the Reset call.
Lines 37 - 40 define types for internal variables:
Border edges of screen
Count_Type type of variable to hold I as defined above
Size_Type type of variable to hold screen dimensions in pixels
Real_Type type of variable used to do real computations
Lines 42 and 43 define two global variables:
Hues the number of colors in a pixel for GraphMode
Fence the borders of the region in use
Lines 45 - 74 form the Arrive procedure that activates the graphics mode and
initializes various variables (Hues and Fence). Fence is adjusted to respect
the screen aspect ratio. Thus the 2 unit radius circle that confines the
MandelBrot set actually would actually appear as a circle on the screen (not
distorted due to pixel height vs. width variation).
Lines 89 through 105 define the Shape record. The auxiliary file VALUES.DAT
consists of one such record.
ForeV the next unprocessed line
RearV the last processed line
H screen width in pixels
V screen height in pixels
Most threshold as defined above
BitPixel BitsPerHue as defined above
PixelByte pixels per byte in GraphMode (equal to 8 divided by
BitPixel)
ByteLine bytes per line in GraphMode (equal to H divided by
PixelByte)
Top real number value of upper edge of region (equal to
Fence[Upper])
Left real number value of left edge of region (equal to
Fence[Leftmost])
YInc corresponding real number size of pixel height
XInc corresponding real number size of pixel width
Weave equal to Interlace as defined above
Lines 107 and 108 declares Seed (the auxiliary data) as a file of Shapes, and
Canvas (the raw graphics data) as an untyped file.
Lines 110 and 110 declare the constants Outgoing and Incoming used the
following procedure Door.
Door at lines 113 - 174 will compile to one of two sections of code depending
on the WEAVE setting (that is, whether the GraphMode uses interlacing). Lines
115 - 141 contain the interlace version and 145 - 172 the non-interlace.
Basically the routines use BlockRead or BlockWrite to load or save the entire
screen to the graphics file (based on the Ingoing/Outgoing parameter). The I/O
is separated into blocks of Block lines (defined above). (Currently the save
routine is used only to pad the graphics file prior to processing but would be
useful for future code that saves images.)
Lines 176 - 179 form an empty subroutine designed for future use of defined
color palette settings.
Lines 181 - 280 form the Cultivate subroutine which contains the complete
processing cycle:
Plant is the name for the first subroutine of the group, lines 186 - 216. This
sets up the two data files. Note that the file variable Notice (representing
the auxiliary file) is created and CLOSEd last to signal readiness to followers
(by Turbo definition any file opened with ReWrite is opened in the "Deny All"
mode).
The procedure Grow (lines 218 - 268) redefines the header of the auxiliary file
as Header. This allows it to monitor the variable AtV which reflects the
current state of processing, using the file variable Eye of type Header. Note
that the file may be inaccessable due to simultaneous access by a FOLLOWER.
The function Ripe (lines 237 - 254) determines whether the file is inaccessable
due to a sharing conflict (in which case it resolves to false) or the actual
disappearance of the file (in which case it returns true signifying
completion). The function also sets the string variable Line with the current
line number so that it can be reported by Grow (line 263). The Delay at line
264 is present so that the leader does not jam the network with requests to
merely monitor the file (the file sharing mode conflicts with FOLLOWER use).
The procedure Harvest (lines 270 - 274) loads the newly completed image.
The procedure NewRegion (lines 282 - 333) allows the user to select a new
region relative to the old in a straightforward way. Cursor keys change the
location of the window. The CTRL key paired with the left and right arrows
changes the size of the window. The Fence variable is updated to reflect the
newly selected region. Note that the aspect ratio is preserved by the use of
the Ratio variable.
Procedure Depart (lines 335 - 339) is designed as the twin of Arrive to handle
proper termination procedure.
- FOLLOWER -
1 {$ifdef CPU87}
2 {$N+}
3 {$endif}
4
5 program Follower;
6
7 uses Graph, Crt, Drivers, Fonts;
8
9 const Choir = 5;
10 Bass = 200;
11 Treble = 1500;
12 Tempo = 2;
13 Rhythm = 40;
14 Path='.';
15 Painting = 'FRACTAL.DAT';
16 Framework = 'VALUES.DAT';
17
18 const No_Error = $00;
19 Not_Found = $02;
20 Access_Denied = $05;
21
22 const Read_Only = $00;
23 Write_Only = $01;
24 Read_Write = $02;
25 Deny_All = $10;
26 Deny_Write = $20;
27 Deny_Read = $30;
28 Deny_None = $40;
29
30 type Count_Type = byte;
31 Size_Type = word;
32
33 {$ifdef CPU87}
34 Real_Type = single;
35 {$else}
36 Real_Type = real;
37 {$endif}
38
39 type Header = record
40 Sound: boolean;
41 AtV,
42 ToV: Size_Type;
43 end;
44
45 type Shape = record
46 Flux: Header;
47 H,
48 V: Size_Type;
49 Most: Count_Type;
50 BitPixel,
51 PixelByte: byte;
52 ByteLine: word;
53 Top,
54 Left,
55 YInc,
56 XInc: Real_Type;
57 Interlace: boolean;
58 end;
59
60 var PriorExit: pointer;
61
62 {$f+}
63 procedure Terminate;
64 {$f-}
65
66 begin
67 CloseGraph;
68 ExitProc:=PriorExit;
69 end;
70
71 const Colors = 3;
72
73 procedure Initiate;
74
75 const Mode: integer = CGAC0;
76 Device: integer= CGA;
77
78 var Result: integer;
79
80 begin
81 Result:=RegisterBGIdriver(@CGADriverProc);
82 InitGraph(Device, Mode, Path);
83 Result:=RegisterBGIfont(@GothicFontProc);
84 Result:=RegisterBGIfont(@TriplexFontProc);
85 Result:=RegisterBGIfont(@SmallFontProc);
86 PriorExit:=ExitProc;
87 ExitProc:=@Terminate;
88 end;
89
90 procedure Inculcate;
91
92 type ColorType = 1..Colors;
93
94 var Hue: array[ColorType] of byte;
95
96 procedure Adjust;
97
98 const BackGround = 7;
99
100 var Index,
101 Cycle: ColorType;
102 Group: array[ColorType] of byte;
103
104 begin
105 Randomize;
106 SetBkColor(Random(Background));
107 for Cycle:=1 to Colors
108 do Group[Cycle]:=Cycle;
109 for Cycle:=Colors downto 1
110 do begin
111 Index:=Random(Cycle) + 1;
112 Hue[Cycle]:=Group[Index];
113 Move(Group[Index + 1], Group[Index], Colors - Index);
114 end;
115 end;
116
117 const AtH = 159;
118 AtV = 66;
119 Offset = 40;
120 FontSize = 4;
121 TitleSize = 5;
122 NameSize = 1;
123
124 type Axes = (X,Y);
125 Pair = array[Axes] of shortint;
126
127 var Height,
128 Cycle: byte;
129
130 const Credit: string = 'Mandelbrot Set';
131 Shift: array[1..8] of Pair = ((-1,-1),(0,-1),(1,1),
132 (-1,0),(1,0),
133 (-1,1),(0,1),(1,1));
134
135 begin
136 Adjust;
137 SetColor(Hue[1]);
138 SetTextJustify(CenterText, CenterText);
139 SetTextStyle(GothicFont, HorizDir, TitleSize);
140 for Cycle:=1 to 8
141 do OutTextXY(AtH + Shift[Cycle][X], AtV + Shift[Cycle][Y], Credit);
142 SetColor(Hue[2]);
143 OutTextXY(AtH, AtV, Credit);
144
145 SetColor(Hue[3]);
146 SetTextStyle(TriplexFont, HorizDir, FontSize);
147 OutTextXY(AtH, AtV - Offset, 'GWNet');
148
149 SetTextStyle(DefaultFont, HorizDir, NameSize);
150 OutTextXY(AtH, AtV + Offset, 'The Mad Programmer strikes again!');
151 end;
152
153 var Seed: Shape;
154
155 procedure Anticipate;
156
157 procedure Respond;
158
159 const Escape = #27;
160
161 var Key: char;
162
163 begin
164 while Keypressed
165 do begin
166 Key:=ReadKey;
167 if Key=Escape
168 then Halt;
169 end;
170 end;
171
172 type States = (Idle, Busy);
173
174 procedure Report(Now: States);
175
176 function NewHue: byte;
177
178 const Hue: byte = 0;
179
180 begin
181 if Hue=Colors
182 then Hue:=1
183 else Inc(Hue);
184 NewHue:=Hue;
185 end;
186
187 const Off = 0;
188 Left = 0;
189 LowLine = 189;
190 FontSize = 4;
191
192 type Note = string[10];
193
194 const Message: array[States] of Note = ('waiting...',
195 'working...');
196
197 begin
198 SetTextStyle(SmallFont, HorizDir, FontSize);
199 SetTextJustify(LeftText, TopText);
200 SetColor(Off);
201 OutTextXY(Left, LowLine, Message[Pred(Now)]);
202 SetColor(NewHue);
203 OutTextXY(Left, LowLine, Message[Now]);
204 end;
205
206 const Time = 500;
207
208 var Notice: file of Shape;
209
210 begin
211 Report(Idle);
212 Assign(Notice, Framework);
213 FileMode:= Read_Write + Deny_None;
214 repeat
215 Respond;
216 Delay(Time);
217 {$i-}
218 Reset(Notice);
219 {$i+}
220 until (IOResult = No_Error);
221 Read(Notice, Seed);
222 Close(Notice);
223 Report(Busy);
224 end;
225
226 procedure Cultivate;
227
228 var Once: boolean;
229 Eye: file of Header;
230
231 function Work: boolean;
232
233 const Front = 0;
234
235 begin
236 FileMode:=Read_Write + Deny_All;
237 repeat
238 {$i-}
239 Reset(Eye);
240 {$i+}
241 until (IOResult = No_Error);
242 Read(Eye, Seed.Flux);
243 Work:=false;
244 with Seed
245 do begin
246 if Once
247 then Dec(Flux.ToV);
248 if Flux.ToV = 0
249 then begin
250 Close(Eye);
251 repeat
252 Erase(Eye);
253 until (IOResult = No_Error);
254 end
255 else begin
256 if Flux.AtV > 0
257 then begin
258 Dec(Flux.AtV);
259 Work:=true;
260 end;
261 Seek(Eye, Front);
262 Write(Eye, Flux);
263 Close(Eye);
264 end;
265 end;
266 end;
267
268 type Pixels = array[byte] of byte;
269 Count_Array = array[byte] of Count_Type;
270
271 var Span,
272 Base,
273 Scale,
274 Middle: word;
275 Map: real;
276 Innate: ^Pixels;
277 Zone: ^Count_Array;
278 Canvas: file;
279
280 procedure Prepare;
281
282 var Range: word;
283
284 const Height = 200;
285
286 begin
287 Assign(Eye, Framework);
288 Assign(Canvas, Painting);
289 with Seed
290 do begin
291 Middle:=V div 2;
292 Map:=Height / V;
293 Range:=(Treble - Bass) div Choir;
294 Scale:=Range div Most;
295 Base:=Bass + Range * Random(Choir);
296 Span:=SizeOf(Count_Type) * H;
297 GetMem(Zone, Span);
298 GetMem(Innate, ByteLine);
299 FileMode:=Write_Only + Deny_None;
300 Reset(Canvas, ByteLine);
301 end;
302 SetWriteMode(XORPut);
303 SetColor(Random(Colors) + 1);
304 end;
305
306 procedure Conclude;
307
308 begin
309 Close(Canvas);
310 FreeMem(Zone, Span);
311 FreeMem(Innate, Seed.ByteLine);
312 SetWriteMode(NormalPut);
313 ClearDevice;
314 end;
315
316 procedure Abandon;
317
318 var Key: char;
319
320 begin
321 NoSound;
322 Conclude;
323 while KeyPressed
324 do Key:=ReadKey;
325 Halt;
326 end;
327
328 procedure Develop;
329
330 procedure Convert;
331
332 var Merge,
333 Inner: byte;
334 Cycle: word;
335 Index: Size_Type;
336
337 begin
338 Cycle:=0;
339 Index:=Seed.H;
340 with Seed
341 do repeat
342 for Inner:=1 to PixelByte
343 do begin
344 Merge:=Merge shl BitPixel or (Most - Zone^[Index]);
345 Dec(Index);
346 end;
347 Innate^[Cycle]:=Merge;
348 Inc(Cycle);
349 until Cycle=ByteLine;
350 end;
351
352 procedure Gauge;
353
354 const Left= 0;
355 Right= 319;
356
357 const Fore: word = 0;
358 Rear: word = 0;
359 PreFore: word = 0;
360 PreRear: word = 0;
361
362 begin
363 with Seed.Flux
364 do begin
365 if Once
366 then Rectangle(Left, PreFore, Right, PreRear);
367 Fore:=Trunc(AtV * Map);
368 Rear:=Trunc(ToV * Map);
369 end;
370 Rectangle(Left, Fore, Right, Rear);
371 PreFore:=Fore;
372 PreRear:=Rear;
373 end;
374
375 var Offset: word;
376
377 const Single = 1;
378
379 begin
380 Convert;
381 with Seed
382 do if Interlace
383 then begin
384 Offset:=Flux.AtV div 2;
385 if Odd(Flux.AtV)
386 then Seek(Canvas, Middle + Offset)
387 else Seek(Canvas, Offset)
388 end
389 else Seek(Canvas, Flux.AtV);
390 BlockWrite(Canvas, Innate^, Single);
391 Gauge;
392 end;
393
394 var ZR,
395 ZI,
396 ZR2,
397 ZI2: Real_Type;
398
399 function Chaotic:boolean;
400
401 begin
402 ZR2:=ZR * ZR;
403 ZI2:=ZI * ZI;
404 Chaotic:=(ZR2 + ZI2 < 4);
405 end;
406
407 var I: Count_Type;
408
409 function Note: word;
410
411 begin
412 Note:=Trunc(Base + I * Scale);
413 end;
414
415 procedure Sing;
416
417 const Cycle: word = 0;
418 Duration: word = 0;
419
420 begin
421 Inc(Cycle);
422 if Cycle=Rhythm
423 then begin
424 Cycle:=0;
425 Duration:=(Rhythm shr Tempo) shl Random(Tempo);
426 Sound(Note);
427 end;
428 if Cycle = Duration
429 then NoSound;
430 end;
431
432 var X: Size_Type;
433 CR,
434 CI: Real_Type;
435
436 begin
437 Prepare;
438 Once:=false;
439 while Work
440 do with Seed
441 do begin
442 X:=H;
443 CR:=Left;
444 CI:=Top - (Flux.AtV * YInc);
445 repeat
446 ZR:=CR;
447 ZI:=CI;
448 I:=1;
449 while Chaotic and (I < Most)
450 do begin
451 ZR:=ZR2 - ZI2 + CR;
452 ZI:=2 * ZR * ZI + CI;
453 Inc(I);
454 end;
455 Dec(X);
456 Zone^[X]:=I;
457 CR:=CR + XInc;
458 if Flux.Sound
459 then Sing;
460 until (X=0);
461 Develop;
462 if KeyPressed
463 then Abandon;
464 Once:=true;
465 end;
466 Conclude;
467 end;
468
469 const EndOfTime = false;
470
471 begin
472 Initiate;
473 repeat
474 Inculcate;
475 Anticipate;
476 Cultivate;
477 until EndOfTime;
478 {Terminate;}
479 end.
Comments
Lines 15 - 31 mirror the same values in LEADER as do lines 39 - 58.
Lines 33 - 37 give an example of the use of the CPU87 conditional directive to
globally change real number types. Likewise, lines 1 - 3 enable 8087
emulation. In this state they may possibly conflict with the LEADER;
Real_Type must match in both programs.
The Terminate procedure is defined in Far mode because it is defined as the new
ExitProc.
Lines 71 - 151 display a title screen and initialize the new error exit
procedure.
Lines 153 - 224 form the Anticipate procedure which waits for the signal from
the LEADER. It allows the program to be aborted with the Escape key in the
wait for the LEADER (in the Respond procedure, lines 157 - 170). Lines 172 -
204 Report on the status of the FOLLOWER. Lines 206 - 224 monitor the current
directory for the VALUES.DAT file. If it exists it is read into the Seed
variable (line 221) in Read_Write and Deny_None mode; this effectively allows
many (but not always all, see line 220) FOLLOWERs to read this file
simultaneously prior to processing with no sharing conflict (actually Read_Only
is all that is necessary).
Lines 226 - 467 form the major procedure Cultivate that comprises the main loop
to process lines.
Lines 438 - 465 hold the main line processing loop along with the Mandelbrot
algorithm. It calls on various subroutines:
The subroutine Prepare (280 - 304) sets up variables. The dynamically
allocated heap variable Zone contains one line of Count_Type variables, and
Innate contains the corresponding line of raw graphics data. It also OPENs the
Canvas untyped file variable to the raw graphics data in Write_Only +
Deny_None. There are never any sharing conflicts with this file because all
data write operations are guaranteed not to overlap.
The function Work gets the status of the parallel processing. If the FOLLOWER
is the last to finish this routine deletes the auxiliary file (at line 252; it
may have to repeat the operation because it can conflict with the monitoring by
the LEADER) and returns false, otherwise it loads the Seed.Flux record (the
section of the auxiliary file that changes) with current unprocessed line
number, updates the auxiliary file, and returns true. Note that the auxiliary
file is opened in Read_Write (for the monitor and update) and Deny_All (to
guarantee synchronization).
The procedure Develop (line 461) converts the Zone array to Innate form, writes
the completed line to the graphics file at lines 379 - 392 (taking care of
interlacing), and gives a visual cue of processing progress (in Gauge, lines
352 - 373).
The procedure Conclude is designed as the twin of Prepare to handle proper
termination procedure. It closes the raw graphics file Canvas (line 309),
deallocates heap variables (lines 310 and 311) and resets graphics defaults
(the WriteMode set by Prepare at 302 for Report at line 201 and Gauge at 370,
and clears the screen at line 313).
Notes
Note that LEADER and FOLLOWER must be executed in the same directory and each
granted appropriate rights (Read, Write, Create, Delete, Search).
LEADER uses the link module Drivers defined by TP that contains all BGI
graphics drivers. Consult the manual for instructions on creating it. It can
contain all drivers but ideally contains only the one defined by GraphDriver.
FOLLOWER uses the link module Fonts in addition, the same notes apply.
LEADER and FOLLOWER have a new and additional approach to visual in conveying
fractal results. A few lines in LEADER and many in FOLLOWER are dedicated to
the process of broadcasting values in SOUND! The LEADER has the capability to
turn sound on and off dynamically (note the Sound variable located in the
dynamic part of the auxiliary file, line 40). There are many values that
change this operation (notably those at the beginning of FOLLOWER in lines 9 -
13) that require some adjustment for a symphony instead of cacophony
(particularly because of varying processor speeds).
What about "fault tolerance"? An unprocessed line results in the display when
any FOLLOWERs are prevented from terminating properly. They can be interrupted
at the end of processing a line with no effect on the whole (except a slower
completion time), note the Abandon procedure in FOLLOWER called at line 463
for this purpose. Any larger degree of tolerance would have to sacrifice the
compact size and form of the auxiliary file. For example, this file could
contain an array of boolean variables that represent the status of every line;
under this scheme an operation is possible where FOLLOWERs could "abscond" at
any time (lose power, be rebooted, etc.) with no effect on the result.
In FOLLOWER the procedure/variable/stack nesting gets fairly deep in the
Cultivate procedure. This is certainly costly in terms of execution time due
to the multiple levels of indirection required to access variables. Also, the
Chaotic function is concise and convenient from a syntactical standpoint but is
costly as well because it represents call overhead in the crucial main loop.
In both programs efficiency was compromised for form.